;;    Copyright ? 2013 Jinson Xu, Gu Zhan, Elizabeth Tang, He Tao
;;    Contact: jinson.xu@nus.edu.sg, samisitspecialist@gmail.com, 
;;             eliz.thf@gmail.com, hetao123@gmail.com

;;    This file is part of Electives-Xpert.
;;    Template and rule definitions are declared here.
;;
;;    Electives-Xpert is free software: you can redistribute it and/or modify
;;    it under the terms of the GNU General Public License as published by
;;    the Free Software Foundation, either version 3 of the License, or
;;    (at your option) any later version.

;;    Electives-Xpert is distributed in the hope that it will be useful,
;;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;    GNU General Public License for more details.

;;    You should have received a copy of the GNU General Public License
;;    along with Electives-Xpert.  If not, see <http://www.gnu.org/licenses/>.

   
;; Template Definitions
    
(deftemplate currentElective
    (slot code ;;elective code
        (type SYMBOL)
        (default ?DERIVE))    
    (slot elective ;;name of current elective
        (type STRING)
        (default ?DERIVE)) 
    (slot category ;;category of current elective
        (type STRING)
        (default ?DERIVE))
    (slot cf ;;cf value
        (type NUMBER)
        (default 0.5))
    (slot cf_career_plans_technical ;;tech inclination data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_career_plans_management ;;management inclination data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_interests_requirements_design_construction ;;interests requirements data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_interests_software_development_platforms_technologies ;;interest software dev data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_interests_it_infrastructure_technology ;;interests IT infra data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_interests_advanced_it_management ;;interests advanced IT mgmt data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_interests_technopreneurship_innovation ;;interests technopreneurship data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_interests_knowledge_engineering_techniques ;;interests knowledge engineering data CF
        (type NUMBER)
        (default 0.5)
    )  
    (slot cf_jobscope_developer ;;jobscope developer data CF
        (type NUMBER)
        (default 0.5)
    )
    (slot cf_jobscope_analyst ;; jobscope analyst data CF
        (type NUMBER)
        (default 0.5)
    )   
    (slot cf_jobscope_architect ;;jobscope architect data CF
        (type NUMBER)
        (default 0.5)
    )   
    (slot cf_jobscope_pm_pmo ;;jobscope pm/pmo data CF
        (type NUMBER)
        (default 0.5)
    )   
    (slot cf_jobscope_it_manager ;;jobscope IT manager data CF
        (type NUMBER)
        (default 0.5)
    )   
    (slot cf_jobscope_network_security ;;jobscope network security data CF
        (type NUMBER)
        (default 0.5)
    )   
    (slot cf_jobscope_others ;;jobscope others data CF
        (type NUMBER)
        (default 0.5)
    )                                                 
)


(deftemplate currentGoal 
    (slot goal ;;name of current goal
        (type STRING)
        (default ?DERIVE)) 
    (slot cf
        (type FLOAT)
        (default 0.5))
)


(deftemplate workingGoal 
    (slot goal ;;name of working goal
        (type STRING)
        (default ?DERIVE)) 
    (slot cf
        (type FLOAT)
        (default 0.5))
)


;; EOF Template Definitions




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Global Variables

(defglobal
    ?*developer-cf* = 0.0
    ?*analyst-cf* = 0.0
    ?*architect-cf* = 0.0
    ?*pm-cf* = 0.0
    ?*it-manager-cf* = 0.0
    ?*network-security-cf* = 0.0
    ?*others-cf* = 0.0
    ?*interests_reqsdesign-cf* = 0.0
    ?*interests_softwaredev-cf* = 0.0
    ?*interests_infratech-cf* = 0.0
    ?*interests_advanceditmgmt-cf* = 0.0
    ?*interests_technoinno-cf* = 0.0
    ?*interests_ke-cf* = 0.0
    ?*recommendation_raw_string* = ""
    ?*recommendation_string* = "")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Rule Definitions


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; CF combination for multiple conclusions RULES
;;; treat this as a black-box
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;combine POSITIVE certainty factors for multiple conclusions
;cf(cf1,cf2) = cf1 + cf2 * (1- cf1)

(defrule combine-positive-cf
  ?f1 <- (currentGoal (goal ?g)(cf ?cf1&:(>= ?cf1 0)))
  ?f2 <- (workingGoal (goal ?g)(cf ?cf2&:(>= ?cf2 0)))
  (test (neq ?f1 ?f2)) ; test pointers and not value
  =>
  (retract ?f2)
  (modify ?f1 (cf =(+ ?cf1 (* ?cf2 (- 1 ?cf1)))))
)

;combine NEGATIVE cf
;cf(cf1,cf2) = cf1 + cf2 * (1 + cf1)

(defrule combine-neg-cf
 (declare (salience -1))
  ?f1 <- (currentGoal   (goal ?g)(cf ?cf1&:(<= ?cf1 0)))
  ?f2 <- (workingGoal (goal ?g)(cf ?cf2&:(<= ?cf2 0)))
  (test (neq ?f1 ?f2))
  =>
  (retract ?f2)
  (modify ?f1 (cf =(+ ?cf1 (* ?cf2 (+ 1 ?cf1)))))
)

;combine one POSITIVE and one NEGATIVE
;cf(cf1,cf2) = (cf1 + cf2) / 1 - MIN[abs(cf1),abs(cf2)]

(defrule neg-pos-cf
 (declare (salience -1))
  ?f1 <- (currentGoal (goal ?g) (cf ?cf1))
  ?f2 <- (workingGoal (goal ?g) (cf ?cf2))
  (test (neq ?f1 ?f2))
  (test (< (* ?cf1 ?cf2) 0))
  =>
  (retract ?f2)
  (modify ?f1 (cf =(/ (+ ?cf1 ?cf2) (- 1 (min (abs ?cf1) (abs ?cf2))))))
)



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; BUSINESS RULES
;;; note that stage1 and stage2 are combined into one assert statement within the defrule
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;**** Rule 1: Ask student for track type
;**** this will increase the CF of your preferred elective
(defrule current_track
    (declare (salience 10))
=>    
    (printout t crlf "What track are you currently taking? (ke|se)") 
    (bind ?response (read))
    (if (eq ?response ke) then
        (assert (student-track ke))
    else
        (assert (student-track se))
    )
        
)



;**** Rule 1a: Ask student for his least interested KE subject
(defrule least_favourite_ke
    (student-track ke) ;; fire when a student-track fact (2nd field of that multifield fact) is ke
=>
    (printout t crlf "What is your least favourite KE subject? Choose 1 of the 5." crlf "1: Business Analytics" crlf "2: Text Mining" crlf "3: Genetic Algorithms" crlf "4: Case Based Reasoning" crlf "5: Knowledge Management" crlf "Your least favourite KE subject is: ")
    (bind ?student_least_favourite_ke_id (read))

    (switch ?student_least_favourite_ke_id
        (case 1 then (assert (student_least_favourite_ke "Business Analytics")))
        (case 2 then (assert (student_least_favourite_ke "Text Mining")))
        (case 3 then (assert (student_least_favourite_ke "Genetic Algorithms")))
        (case 4 then (assert (student_least_favourite_ke "Case Based Reasoning")))
        (case 5 then (assert (student_least_favourite_ke "Knowledge Management")))
    )
)


;**** Rule 1b: Update CF of KE modules
(defrule current_track_update_ke
    (student-track ?track)
    (student_least_favourite_ke ?least_favourite_ke)
    (currentElective (elective ?name) (category "Knowledge Engineering Techniques") (cf ?cf))
=>
    (if (eq ?track ke) then
        (printout t crlf "Current track is: " ?track ". Giving KE modules priority now...")
        (if (neq ?name ?least_favourite_ke) then 
            (assert (workingGoal (goal ?name) (cf (* ?cf 1.8))))
        )
    )
)




;**** Rule 2: Ask student for his main purpose of study
(defrule study_purpose
    (or (student_least_favourite_ke ?lfke) (student-track se))
=>
    (printout t crlf "Are you taking this course mainly out of interest? (0 to 1): ")
    (assert (course-purpose-interest-rating (read)))
    (printout t crlf "Are you taking this course mainly to advance your career? (0 to 1): ")
    (assert (course-purpose-career-rating (read)))
)



;**** Rule 3: Ask student for his career goal's inclination
; Input is the evidence/data CF for rules such as 
; "if technical, then software requirement engineering" (0.13333)
; so if input = 0.7 then
; CF = 0.7 * 0.1333 = 0.09331
(defrule career_goal
    (student-track ?track)
    (course-purpose-career-rating ?career_rating)
=>
    (printout t crlf "Are your career plans for the next 3 to 5 years mainly technically inclined? (0 to 1): ")
    (assert (career-goal-tech-inclination (read)))
    (printout t crlf "Are your career plans for the next 3 to 5 years mainly management inclined? (0 to 1): ")
    (assert (career-goal-management-inclination (read)))
)


;**** Rule 3a: Update all workingGoals with career_goal tech inclination data CF modifications
(defrule update_with_tech_inclination
    (student-track ?track)
    (course-purpose-career-rating ?career_rating)
    (career-goal-tech-inclination ?career_tech_inclination)
    (currentElective (elective ?name) (cf_career_plans_technical ?cf_cp_tech)) ; get the RULE CF for "IF TECH INCLINED (data CF) then XX elective (will be able to help you in your career goal)     
=>
    (assert (workingGoal (goal ?name) (cf (* (min ?career_rating ?career_tech_inclination) ?cf_cp_tech))))
)



;**** Rule 3b: Update all workingGoals with career_goal management inclination data CF modifications
(defrule update_with_management_inclination
    (student-track ?track)
    (course-purpose-career-rating ?career_rating)
    (career-goal-management-inclination ?career_man_inclination)
    (currentElective (elective ?name) (cf_career_plans_management ?cf_cp_man)) ; get the RULE CF for "IF MANAGEMENT INCLINED (data CF) then XX elective (will be able to help you in your career goal)"      
=>
    (assert (workingGoal (goal ?name) (cf (* (min ?career_rating ?career_man_inclination) ?cf_cp_man))))
)




;**** Rule 4: Ask student for his main 3 job roles 
(defrule current_job_roles
    (student-track ?track)
    (career-goal-tech-inclination ?career_tech_inclination)
=>
    (printout t crlf "What are your current 3 main job roles in descending order of relevance? Type in job role IDs separated by spaces. eg.(1 3 5)" crlf "1: Developer" crlf "2: Analyst" crlf "3: Architect" crlf "4: PM/PMO" crlf "5: IT Manager" crlf "6: Network/Security" crlf "7: Others" crlf "Your 3 main jobscopes: ")
    (assert (jobroles_string (readline)))    
)


;**** Rule 4a: Update job roles data CF with the ranked jobscopes that user entered.
(defrule current_job_roles_cf
    (jobroles_string ?jobroles_string)
=>
    (bind ?jobroles_array (str-explode ?jobroles_string))
        
    (loop-for-count(?cnt 1 3) do
        (bind ?jobid (nth$ ?cnt ?jobroles_array))
        (printout t crlf ?cnt ": " (nth$ ?cnt ?jobroles_array))
        
        (switch ?cnt
            (case 1 then(bind ?newcf 0.5))
            (case 2 then (bind ?newcf 0.3))
            (case 3 then (bind ?newcf 0.2))
        )
              
        (switch ?jobid
            (case 1 then (bind ?*developer-cf* ?newcf))
            (case 2 then (bind ?*analyst-cf* ?newcf))
            (case 3 then (bind ?*architect-cf* ?newcf))
            (case 4 then (bind ?*pm-cf* ?newcf))
            (case 5 then (bind ?*it-manager-cf* ?newcf))
            (case 6 then (bind ?*network-security-cf* ?newcf))
            (case 7 then (bind ?*others-cf* ?newcf))
        )        
    )
    (assert (job_roles_cf_set yes));                                       
)


;**** Rule 4a1: Update all workingGoals with all skillset CF modifications
(defrule update_with_skillsets
    (job_roles_cf_set ?jcfSet)
    (currentElective (elective ?name) (cf_jobscope_developer ?devruleCF) (cf_jobscope_analyst ?anaruleCF) (cf_jobscope_architect ?archruleCF) (cf_jobscope_pm_pmo ?pmruleCF) (cf_jobscope_it_manager ?itmruleCF) (cf_jobscope_network_security ?nsruleCF) (cf_jobscope_others ?oruleCF)) ; get the RULE CF for        
=>
    (assert (workingGoal (goal ?name) (cf (* ?*developer-cf* ?devruleCF))))
    (assert (workingGoal (goal ?name) (cf (* ?*analyst-cf* ?anaruleCF))))
    (assert (workingGoal (goal ?name) (cf (* ?*architect-cf* ?archruleCF))))
    (assert (workingGoal (goal ?name) (cf (* ?*pm-cf* ?pmruleCF))))
    (assert (workingGoal (goal ?name) (cf (* ?*it-manager-cf* ?itmruleCF))))
    (assert (workingGoal (goal ?name) (cf (* ?*network-security-cf* ?nsruleCF))))
    (assert (workingGoal (goal ?name) (cf (* ?*others-cf* ?oruleCF))))        
)




;**** Rule 5: Ask student what his top 3 interest categories are 
(defrule main_interest_categories
    (student-track ?track)
    (career-goal-tech-inclination ?career_tech_inclination)
    (job_roles_cf_set ?jcfSet)    
=>
    (printout t crlf "What are your top 3 interest categories in descending order of preference? Type in interest category IDs separated by spaces. eg.(1 3 5)" crlf "1: Requirements, Design & Construction" crlf "2: Software Development Platforms & Technologies" crlf "3: IT Infrastructure Technology" crlf "4: Advanced IT Management" crlf "5: Technopreneurship & Innovation" crlf "6: Knowledge Engineering Techniques" crlf "Your 3 main interests: ")
    (assert (interests_string (readline)))    
)


;**** Rule 5a: Update interests data CF with the ranked interest categories that user entered.
(defrule interest_categories_cf
    (interests_string ?interests_string)
=>
    (bind ?interests_array (str-explode ?interests_string))
        
    (loop-for-count(?cnt 1 3) do
        (bind ?interestid (nth$ ?cnt ?interests_array))
        
        (switch ?cnt
            (case 1 then(bind ?newcf 0.5))
            (case 2 then (bind ?newcf 0.3))
            (case 3 then (bind ?newcf 0.2))
        )
        
        (switch ?interestid
            (case 1 then (bind ?*interests_reqsdesign-cf* ?newcf))
            (case 2 then (bind ?*interests_softwaredev-cf* ?newcf))
            (case 3 then (bind ?*interests_infratech-cf* ?newcf))
            (case 4 then (bind ?*interests_advanceditmgmt-cf* ?newcf))
            (case 5 then (bind ?*interests_technoinno-cf* ?newcf))
            (case 6 then (bind ?*interests_ke-cf* ?newcf))
        )        
    )
    (assert (interests_cf_set yes));                                       
)


;**** Rule 5a1: Update all workingGoals with interest categories CF modifications
(defrule update_with_interest_categories
    (course-purpose-interest-rating ?interest-rating) ;; level of confidence that the student's taking the course due to interest
    (interests_cf_set ?interestsCFset) ;; control fact
    (currentElective (elective ?name) (cf_interests_requirements_design_construction ?reqsruleCF) (cf_interests_software_development_platforms_technologies ?softwaredevCF) (cf_interests_it_infrastructure_technology ?itinfraCF) (cf_interests_advanced_it_management ?aitCF) (cf_interests_technopreneurship_innovation ?techinnoCF) (cf_interests_knowledge_engineering_techniques ?keCF))
=>
    (assert (workingGoal (goal ?name) (cf (* (min ?*interests_reqsdesign-cf* ?interest-rating) ?reqsruleCF)))) ;; stage 1 and 2 for interests premises to elective conclusions
    (assert (workingGoal (goal ?name) (cf (* (min ?*interests_softwaredev-cf* ?interest-rating) ?softwaredevCF)))) ;; stage 1 and 2 for interests premises to elective conclusions
    (assert (workingGoal (goal ?name) (cf (* (min ?*interests_infratech-cf* ?interest-rating) ?itinfraCF)))) ;; stage 1 and 2 for interests premises to elective conclusions
    (assert (workingGoal (goal ?name) (cf (* (min ?*interests_advanceditmgmt-cf* ?interest-rating) ?aitCF)))) ;; stage 1 and 2 for interests premises to elective conclusions
    (assert (workingGoal (goal ?name) (cf (* (min ?*interests_technoinno-cf* ?interest-rating) ?techinnoCF)))) ;; stage 1 and 2 for interests premises to elective conclusions               
    (assert (workingGoal (goal ?name) (cf (* (min ?*interests_ke-cf* ?interest-rating) ?keCF)))) ;; stage 1 and 2 for interests premises to elective conclusions       
)





;**** Rule 6: Populate raw results string
(defrule populate_raw_results_string
    (declare (salience -10))
    (currentGoal (goal ?name) (cf ?cf))
=>    
    (bind ?*recommendation_raw_string* (str-cat ?*recommendation_raw_string* " " ?name " " ?cf)) ;;for each currentGoal fact, we simply concat the name and cf into the raw results string global variable
)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Function Definitions

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;(deffunction sort_two_electives (?n1 ?v1 ?n2 ?v2)
;    (if (?v1 < $v2) then
;;        (create$ ?n1 ?v1 ?n2 ?v2) else 
;        (create$ ?n2 ?v2 ?n1 ?v1)
;    )
;)